package com.xiaominge.utils.excleUtils.excelInput;

import com.alibaba.fastjson.JSON;
import com.xiaominge.exception.ParameterRuntimeException;
import com.xiaominge.utils.excleUtils.excelInput.annotation.ExcelField;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Supplier;

/**
 * 根据对象中的注解 进行导入的  标题就注解的值
 * 查看注解
 * {@link com.xiaominge.utils.excleUtils.excelInput.annotation.ExcelField}
 *
 * @param <T>
 */
@Slf4j
public class ExcelAnnotationUtils<T> {

    /**
     * 实体修改创建方法
     */
    Supplier<T> supplier;
    /**
     * index 和属性的对应关系  不用每次都循环找
     */
    private Map<Integer, Field> fieldMap;

    /**
     * Excel sheet最大行数，默认65536
     */
    public static final int sheetSize = 65536;
    private static final DateTimeFormatter DATE_TIME_FORMATTER_ALL = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final DateTimeFormatter DATE_FORMATTER_ALL = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    /**
     * excel注释跑龙套
     *
     * @param
     * @param supplier t建设者 对象的构建方法 正常是构造方法 内部内的话 就是创建内部内的创创建方法
     */
    public ExcelAnnotationUtils(Supplier<T> supplier) {
        Map<Integer, Field> fieldMap = getFieldMap((Class<T>) supplier.get().getClass());
        if (!fieldMap.isEmpty()) {
            this.fieldMap = fieldMap;
            this.supplier = supplier;
        } else {
            throw ParameterRuntimeException.throwException("对象中至少要有一个字段有 ExcelField 注解");
        }

    }

    /**
     * excel的导入  注解版  对象中一定要有注解
     * excel 中字段类型要和对象中的类型匹配  如果不匹配
     * 可以参考读取为hashMap 的方法 查看下面犯法
     * {@link ExcelInputUtils#readToListMap(java.io.InputStream, java.lang.String...) }
     *
     * @param inputStream 输入流
     * @return {@link List}<{@link T}> 返回指定类型
     * @throws Exception 异常
     */
    public List<T> importExcel(InputStream inputStream) throws Exception {
        Workbook workbook = WorkbookFactory.create(inputStream);
        Sheet sheet = workbook.getSheetAt(0);
        List<T> dataList = new ArrayList<T>();
        for (int i = 1; i <= sheet.getLastRowNum(); i++) {
            Row row = sheet.getRow(i);
            if (row == null) {
                continue;
            }
            int maxRowNum = sheet.getPhysicalNumberOfRows();
            if (maxRowNum <= 1) {
                throw ParameterRuntimeException.throwException("导入数据不能为空");
            }
            if (sheetSize < maxRowNum) {
                throw ParameterRuntimeException.throwException("单次导入数据最大可支持" + sheetSize + "行");
            }
            //获取最小列
            int minCell = row.getFirstCellNum();
            //获取最大列
            int maxCell = row.getLastCellNum();
            T obj;
            obj = supplier.get();
            //循环每行单元格
            int rowNullNum = 0; //
            boolean rowNull = false;
            for (int cellNum = minCell; cellNum <= maxCell; cellNum++) {
                Cell cell = row.getCell(cellNum);
                if (cell == null || "".equals(cell.toString())) {
                    rowNullNum++;
                    if (rowNullNum == maxCell) {  //如果一行单元格都是空格 当做空行处理
                        rowNull = true;
                        break;
                    }
                    continue;
                }
                Field field = fieldMap.get(minCell == 0 ? (cellNum + 1) : cellNum);
                if (field == null) {
                    continue;
                }
                field.setAccessible(true);

                Object cellValue = getCellValue(cell);
                if (cellValue != null) {
                    //todo 字段读取的类型 可以在这里进行修改匹配
                  //  String cellValueTypeName = cellValue.getClass().getTypeName();

                    //替换为字段的类型 转换excel 里面值的类型
                    String cellValueTypeName=field.getType().getTypeName();
                    String cellValueString = cellValue.toString();

                    ExcelField excelField = field.getAnnotation(ExcelField.class);
                    ExcelField.DataType dataType = excelField.type();
                    switch (dataType) {
                        case String:
                            field.set(obj, cellValueString);
                        case Number:
                            try {
                                field.setInt(obj, Integer.valueOf(cellValueString)); ///int 类型
                            } catch (Exception e) {
                                try {
                                    field.setDouble(obj, Double.valueOf(cellValueString)); //double
                                } catch (Exception e1) {
                                    field.setFloat(obj, Float.valueOf(cellValueString)); //float
                                }
                            }
                            break;
                        case Date:
                            if (Objects.equals(LocalDateTime.class.getName(), cellValueTypeName)) {
                                field.set(obj, LocalDateTime.parse(cellValueString, DATE_TIME_FORMATTER_ALL));

                            } else if (Objects.equals(LocalDate.class.getName(), cellValueTypeName)) {
                                field.set(obj, LocalDate.parse(cellValueString, DATE_FORMATTER_ALL));

                            } else if (Objects.equals(Date.class.getName(), cellValueTypeName)) {
                                field.set(obj, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(cellValueString));
                            } else {
                                throw new RuntimeException("excel 导入 时间格式错误");
                            }
                            break;
                        case no:
                            if (Objects.equals(LocalDateTime.class.getName(), cellValueTypeName)) {
                                field.set(obj, LocalDateTime.parse(cellValueString, DATE_TIME_FORMATTER_ALL));

                            } else if (Objects.equals(LocalDate.class.getName(), cellValueTypeName)) {
                                field.set(obj, LocalDate.parse(cellValueString, DATE_FORMATTER_ALL));

                            } else if (Objects.equals(Date.class.getName(), cellValueTypeName)) {
                                field.set(obj, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(cellValueString));

                            } else if (Objects.equals(Integer.class.getName(), cellValueTypeName) || (Objects.equals(int.class.getName(), cellValueTypeName))) {
                                field.set(obj, Integer.valueOf(cellValueString));

                            } else if (Objects.equals(Double.class.getName(), cellValueTypeName) || (Objects.equals(double.class.getName(), cellValueTypeName))) {
                                field.setDouble(obj, Double.valueOf(cellValueString));

                            } else if (Objects.equals(Float.class.getName(), cellValueTypeName) || (Objects.equals(Float.class.getName(), cellValueTypeName))) {
                                field.setFloat(obj, Float.valueOf(cellValueString));
                            } else if (Objects.equals(Character.class.getName(), cellValueTypeName) || (Objects.equals(char.class.getName(), cellValueTypeName))) {
                                field.setChar(obj, cellValueString.charAt(0)); //只转 字符串第一位

                            } else if (Objects.equals(Boolean.class.getName(), cellValueTypeName) || (Objects.equals(boolean.class.getName(), cellValueTypeName))) {
                                field.setBoolean(obj, Boolean.parseBoolean(cellValueString));

                            } else if (Objects.equals(Byte.class.getName(), cellValueTypeName) || (Objects.equals(byte.class.getName(), cellValueTypeName))) {
                                field.set(obj, Byte.valueOf(cellValueString));
                            } else {
                                //默认写toString 的内容
                                field.set(obj, cellValue.toString());
                            }
                    }
                } else {
                    field.set(obj, null);
                }

            }
            if (rowNull) {
                continue;
            }
            dataList.add(obj);

        }
        log.info(JSON.toJSONString(dataList));
        return dataList;
    }


    /**
     * 下载模板
     *
     * @param fileName 下载模板名称
     */
    public void downloadTemplateExcel(HttpServletResponse response, String fileName) {
        try {
            fileName = new String(fileName.getBytes(), "ISO8859-1");
            response.reset();
            response.setHeader("Content-disposition", "attachment; filename=" + fileName + ".xls");
            response.setContentType("application/x-xls");
            downloadTemplateExcel(response.getOutputStream(), ExcelType.xlsx);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 下载excel模板
     * 下载模板
     *
     * @param outputStream 输出流
     */
    public void downloadTemplateExcel(OutputStream outputStream, ExcelType excelType) {
        //1、创建workbook，对应一个excel
        Workbook wb = null;
        if (ExcelType.xls == excelType) {
            wb = new HSSFWorkbook();
        } else if (ExcelType.xlsx == excelType) {
            wb = new XSSFWorkbook();
        } else {
            ParameterRuntimeException.throwException("excel 模板类型错误");
        }

        //首先创建字体样式
        Font font = wb.createFont();//创建字体样式
        font.setFontName("宋体");//使用宋体
        font.setFontHeightInPoints((short) 10);//字体大小
        font.setBold(true);    // 加粗
        font.setColor(IndexedColors.BLACK.index);

        CellStyle cellStyle = wb.createCellStyle();
        //设置前景色填充方式颜色
        cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        //设置前景色颜色
        cellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        cellStyle.setFont(font);//将字体注入
        cellStyle.setWrapText(true);// 自动换行
        cellStyle.setAlignment(HorizontalAlignment.CENTER);// 左右居中
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);// 上下居中
        cellStyle.setBorderTop(BorderStyle.THIN);// 边框的大小
        cellStyle.setBorderBottom(BorderStyle.THIN);
        cellStyle.setBorderLeft(BorderStyle.THIN);
        cellStyle.setBorderRight(BorderStyle.THIN);
        //生成一个sheet，对应excel的sheet，参数为excel中sheet显示的名字
        Sheet sheet = wb.createSheet("sheet");
        sheet.setColumnWidth(0, 0);
        Row row = sheet.createRow(0);
        row.setHeight((short) 800);// 设定行的高度
        Cell cell = row.createCell(0);//我们第一列设置宽度为0，不会显示，因此第0个单元格不需要设置样式
        for (int i = 1; i <= fieldMap.size(); i++) {
            Field field = fieldMap.get(i);
            ExcelField annotation = field.getAnnotation(ExcelField.class);
            cell = row.createCell(annotation.index());
            cell.setCellValue(annotation.title());
            cell.setCellStyle(cellStyle);
        }
        try {
            wb.write(outputStream);
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * key :headName  val:该名称对应的字段
     *
     * @param clazz
     * @return
     */
    private Map<Integer, Field> getFieldMap(Class<? extends T> clazz) {
        Field[] fields = clazz.getDeclaredFields();
        Map<Integer, Field> fieldMap = new HashMap<>();
        for (Field field : fields) {
            if (field.isAnnotationPresent(ExcelField.class)) {
                ExcelField annotation = field.getAnnotation(ExcelField.class);
                fieldMap.put(annotation.index(), field);
            }
        }

        return fieldMap;
    }


    /**
     * 根据excel单元格类型 获取 单元格值
     *
     * @param cell
     */
    public static Object getCellValue(Cell cell) {
        Object value;
        switch (cell.getCellType()) {
            case NUMERIC:
                //判断cell是否为日期格式
                if (DateUtil.isCellDateFormatted(cell)) {
                    //转换为日期格式YYYY-mm-dd
                    value = cell.getDateCellValue();

                } else {
                    double numericCellValue = cell.getNumericCellValue();
                    BigDecimal bdVal = new BigDecimal(numericCellValue);
                    if ((bdVal + ".0").equals(Double.toString(numericCellValue))) {
                        // 整型
                        value = bdVal.intValue();
                    } else if (String.valueOf(numericCellValue).contains("E10")) {
                        // 科学记数法 返回字符串 类型
                        value = new BigDecimal(numericCellValue).toPlainString();
                    } else {
                        // 浮点型
                        value = numericCellValue;
                    }
                }
                break;
            case STRING:
                value = cell.getRichStringCellValue().getString();
                break;
            case FORMULA:
                value = cell.getCellFormula();
                break;
            case BOOLEAN:
                value = cell.getBooleanCellValue();
                break;
            default:
                return null;
        }
        return value;
    }


    @Getter
    public enum ExcelType {
        xlsx,
        xls;
    }
}
